package org.flexthinker.binding.form.support
{
    import org.flexthinker.binding.value.ValueModel;

    import org.flexthinker.binding.value.support.BufferedValueModel;

    import mx.events.PropertyChangeEvent;

    /**
     * Default form model implementation.
     */
    public class DefaultFormModel extends AbstractFormModel
    {
        private var dirtyPropertyValueModels:Vector.<ValueModel> = new Vector.<ValueModel>();

        public function DefaultFormModel( domainObject:Object, buffered:Boolean = true ):void
        {
            // it should be always buffered in order to check for isDirty
            super( domainObject, buffered );
        }


        override public function isDirty():Boolean
        {
            return dirtyPropertyValueModels.length > 0;
        }

        override protected function preProcessNewValueModel( formProperty:String, valueModel:ValueModel ):ValueModel
        {

            if( ! (valueModel is ValidatingFormValueModel) )
            {
                return new ValidatingFormValueModel( formProperty, valueModel, true, propertyChange );
            }
            return valueModel;
        }


        override protected function postProcessNewValueModel( formProperty:String, valueModel:ValueModel ):void
        {
            validateAfterPropertyChanged( formProperty );
        }


        private function propertyChange(formProperty:String, event:PropertyChangeEvent ):void
        {
            validateAfterPropertyChanged( formProperty, event );
        }

        protected function validateAfterPropertyChanged( formProperty:String, event:PropertyChangeEvent = null ):void
        {
            if ( formProperty )
            {
                invalidateDirtyFlagForProperty(formProperty);
            }
        }

        private function invalidateDirtyFlagForProperty( formProperty:String ):void
        {
            var currentValueModel:ValidatingFormValueModel = ValidatingFormValueModel(getValueModel( formProperty ));
            var idx:int = dirtyPropertyValueModels.indexOf( currentValueModel );

            if ( currentValueModel.getWrappedValueModel() is BufferedValueModel )
            {
                var bvm:BufferedValueModel = BufferedValueModel( currentValueModel.getWrappedValueModel() );
                if ( bvm.isBuffering() && idx < 0 )
                {
                    dirtyPropertyValueModels.push( currentValueModel );
                }
                if ( !bvm.isBuffering() && idx >= 0 )
                {
                    dirtyPropertyValueModels.splice(idx, 1);
                }
            }
        }
    }
}

import org.flexthinker.binding.value.PropertyChangeListener;
import org.flexthinker.binding.value.ValueModel;
import org.flexthinker.binding.value.ValueModelProxy;
import org.flexthinker.binding.value.support.AbstractValueModelProxy;
import org.flexthinker.binding.value.support.DelegatingPropertyChangeListener;

import mx.events.PropertyChangeEvent;

class ValidatingFormValueModel extends AbstractValueModelProxy implements ValueModel, ValueModelProxy
{
    private var valueChangeListener:PropertyChangeListener;
    private var formProperty:String;
    private var valueChangeClosure:Function;

    public function ValidatingFormValueModel( formProperty:String, model:ValueModel, validateOnChange:Boolean, valueChangeClosure:Function
        ):void
    {
        super( model );
        this.formProperty = formProperty;
        this.valueChangeClosure = valueChangeClosure;
        init( validateOnChange );
    }

    private function init( validateOnChange:Boolean ):void
    {
        if( validateOnChange )
        {
            valueChangeListener = new DelegatingPropertyChangeListener( valueChangeHandler );
            addValueChangeListener( valueChangeListener );
        }
    }

    public function getFormProperty():String
    {
        return formProperty;
    }

    private function valueChangeHandler(event:PropertyChangeEvent):void
    {
        valueChangeClosure(formProperty, event);
    }

}